#include "memory_pool.hpp"

namespace mina
{
	MemoryPool::MemoryPool(int page_size, int capacity_size)
		: _head(-1),
		_free_block(0),
		_block_size(page_size),
		_block_num(capacity_size / page_size),
		_base(0)
	{
		assert(page_size > 0 && _block_num > 0);

		assert((_base = (char*)malloc(_block_size * _block_num)) != NULL);

		for (int i = 0; i < (_block_num - 1); ++ i)
		{
			*((int*)(get_addr(i))) = (i + 1);
		}

		*((int*)(get_addr(_block_num - 1))) = -1;

		_head = 0;
		_free_block = _block_num;
		_end = _base + _block_size * _block_num;
	}

	MemoryPool::~MemoryPool()
	{
		::free(_base);
		_base = 0;
		_head = -1;
		_free_block = 0;
		_end = 0;
	}

	void* MemoryPool::allocate()
	{
		if (_head == -1)
		{
			return NULL;
		}

		void * addr = get_addr(_head);
		_head = *((int*)(addr));
		-- _free_block;

		return addr;
	}

	void MemoryPool::deallocate(void * addr)
	{
		int index = get_index(addr);
		assert(index >= 0 && index < _block_num);
		*((int*)(addr)) = _head;
		_head = index;
		++ _free_block;
	}

	void * MemoryPool::get_addr(int index)
	{
		assert(index >= 0 && index < _block_num);

		return (void*)(_base + (index * _block_size));
	}

	int MemoryPool::get_index(void * addr)
	{
		assert(addr >= _base && addr < _end);
		assert((((char*)addr - _base) % _block_size) == 0);

		return (((char*)addr - _base) / _block_size);
	}

	void MemoryPool::display()
	{
		for (int head = _head; head != -1;)
		{
			std::cout << head << "\t";
			head = *((int*)get_addr(head));
		}
		std::cout << std::endl;
	}
}

